
// 异步日志器的实现
// 双缓冲区思想
// 外界将数据传入缓冲区中
// 异步线程对处理缓冲区中的数据进行处理，若处理缓冲区里没有数据了则交换缓冲区
// 成员：
// 1.双缓冲区：生产，消费
// 2.互斥锁：保证线程安全
// 3.条件变量：生产缓冲区里没有数据，处理完消费缓冲区后就休眠
// 4.回调函数：外部传入一个函数，告诉异步工作器数据该如何处理
// 4.1：停止异步工作器
// 4.2：添加数据到缓冲区
// 5.工作器状态
// 6.线程
// 7.异步日志器模式
// 私有操作：
// 创建线程，线程入口函数中，交换缓冲区，对消费缓冲区数据使用回调函数，处理完后再次交换

#ifndef __LOOPER__
#define __LOOPER__

#include "buffer.hpp"
#include <mutex>
#include <condition_variable>
#include <functional>
#include <thread>
#include <memory>
#include <atomic>

namespace log
{
    using Functor = std::function<void(buffer &)>;
    enum class async_type
    {
        ASYNC_SAFE,//安全模式，缓冲区固定大小，满了则阻塞
        ASYNC_UNSAFE//不安全模式，缓冲区满了就扩容，用于极限性能测试
    };
    class async_looper
    {
    public:
        using ptr = std::shared_ptr<async_looper>;

        async_looper
        (
            const Functor& call_back,
            async_type async_type = async_type::ASYNC_SAFE
        ):
            _call_back(call_back)
            ,_thread(std::thread(&async_looper::thread_enter, this))
            ,_stop(false)
            ,_async_type(async_type)
        {}

        ~async_looper() {stop();}

        // 生产者
        void push(const char *data, size_t len)
        {
            // 生产者
            std::unique_lock<std::mutex> lock(_mutex);
            if(_async_type == async_type::ASYNC_SAFE)
            {
                // 生产者条件变量：若生产者缓冲区有足够空间，则进行生产
                _pro_cond.wait(lock, [&](){return _pro_buf.writeable_size() >= len;});
            }
            // 进行生产：向生产者缓冲区添加数据
            _pro_buf.push(data, len);
            // 唤醒消费者
            _con_cond.notify_all();
        }

        void stop()
        {
            _stop = true;
            // 唤醒所有消费者线程（其实就1个）
            _con_cond.notify_all();//须在消费者条件变量里再添加一个条件
            _thread.join();
        }

    private:
        // 线程入口函数中，交换缓冲区，对消费缓冲区数据使用回调函数，处理完后再次交换
        // 消费者
        void thread_enter()
        {
            // 消费者
            while(1)
            {
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    // stop被设置为true且生产者缓冲区为空方可退出
                    // 否则生产者缓冲区的数据会丢失
                    if(_stop == true && _pro_buf.empty()) break;
                    // 消费者条件变量：如果生产者缓冲区不为空，则进行消费，反正则阻塞
                    _con_cond.wait(lock, [&](){return _stop || !_pro_buf.empty();});
                    // 进行消费：交换缓冲区
                    _con_buf.swap(_pro_buf);
                    if(_async_type == async_type::ASYNC_SAFE)
                    {
                        // 唤醒生产者
                        _pro_cond.notify_all();
                    }
                }
                // 对消费者缓冲区数据进行处理
                _call_back(_con_buf);// logger里的real_log
                // 处理完成后重置缓冲区
                _con_buf.reset();
            }
        }

    private:
        async_type _async_type;//异步日志器模式
        buffer _pro_buf;
        buffer _con_buf;
        std::mutex _mutex;
        std::condition_variable _pro_cond;
        std::condition_variable _con_cond;
        Functor _call_back;//外部传入一个函数，告诉异步工作器数据该如何处理
        std::atomic<bool> _stop;
        std::thread _thread;
    };
}

#endif